﻿using Dicide.ViewModels;
using System;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace Dicide.Views
{
    public sealed partial class MyCanvas : UserControl
    {
        struct Cell
        {
            public Cell(Point point)
            {
                Col = (int)(point.X / CellSize);
                Row = (int)(point.Y / CellSize);
            }

            public Cell(UIElement element)
                : this(new Point(Canvas.GetLeft(element), Canvas.GetTop(element)))
            {
            }

            public int Row;
            public int Col;
            public const int CellSize = 100;

            public void SetCell(UIElement element)
            {
                Canvas.SetLeft(element, Col * CellSize);
                Canvas.SetTop(element, Row * CellSize);
            }

            public static bool operator ==(Cell cell1, Cell cell2)
            {
                return cell1.Row == cell2.Row && cell1.Col == cell2.Col;
            }

            public static bool operator !=(Cell cell1, Cell cell2)
            {
                return cell1.Row != cell2.Row || cell1.Col != cell2.Col;
            }

            public Cell Left
            {
                get { return new Cell { Row = Row, Col = Col - 1 }; }
            }

            public Cell Top
            {
                get { return new Cell { Row = Row - 1, Col = Col }; }
            }

            public Cell Right
            {
                get { return new Cell { Row = Row, Col = Col + 1 }; }
            }

            public Cell Bottom
            {
                get { return new Cell { Row = Row + 1, Col = Col }; }
            }
        }

        public MyCanvas()
        {
            this.InitializeComponent();
            _grid.SizeChanged += OnGridSizeChanged;
            _canvas.Tapped += OnCanvasTapped;
        }

        void OnCanvasTapped(object sender, TappedRoutedEventArgs e)
        {
            var instance = (SelectedComponent != null) ? Activator.CreateInstance(SelectedComponent.GetType()) as FrameworkElement : null;
            SetChild(e.GetPosition(this), instance);
            OnCircuitChanged();
        }

        void SetChild(Point point, FrameworkElement element)
        {
            var cell = new Cell(point);

            if (element != null)
            {
                var previousElement = FindChild(cell);
                if (previousElement != null)
                {
                    if (previousElement.GetType().Equals(element.GetType()))
                    {
                        return;
                    }
                }
            }

            RemoveChild(cell);

            if (element != null)
            {
                AddChild(cell, element);

                var center = element.DataContext as ComponentViewModel;

                if (center != null)
                {
                    var left = FindChildComponentViewModel(cell.Left);
                    if (left != null)
                    {
                        center.Left = left;
                        left.Right = center;
                    }

                    var top = FindChildComponentViewModel(cell.Top);
                    if (top != null)
                    {
                        center.Top = top;
                        top.Bottom = center;
                    }

                    var right = FindChildComponentViewModel(cell.Right);
                    if (right != null)
                    {
                        center.Left = right;
                        right.Left = center;
                    }

                    var bottom = FindChildComponentViewModel(cell.Bottom);
                    if (bottom != null)
                    {
                        center.Bottom = bottom;
                        bottom.Top = center;
                    }
                }
            }
        }

        void AddChild(Cell cell, FrameworkElement element)
        {
            cell.SetCell(element);
            _canvas.Children.Add(element);
            StartWatchingChild(element.DataContext as ComponentViewModel);
        }

        FrameworkElement FindChild(Cell cell)
        {
            foreach (var child in _canvas.Children)
            {
                if (new Cell(child) == cell)
                {
                    return child as FrameworkElement;
                }
            }

            return null;
        }

        ComponentViewModel FindChildComponentViewModel(Cell cell)
        {
            var view = FindChild(cell);

            if (view != null)
            {
                return view.DataContext as ComponentViewModel;
            }

            return null;
        }

        void RemoveChild(Cell cell)
        {
            var element = FindChild(cell);

            if (element != null)
            {
                var center = element.DataContext as ComponentViewModel;

                if (center != null)
                {
                    var left = FindChildComponentViewModel(cell.Left);
                    if (left != null)
                    {
                        center.Left = null;
                        left.Right = null;
                    }

                    var top = FindChildComponentViewModel(cell.Top);
                    if (top != null)
                    {
                        center.Top = null;
                        top.Bottom = null;
                    }

                    var right = FindChildComponentViewModel(cell.Right);
                    if (right != null)
                    {
                        center.Right = null;
                        right.Left = null;
                    }

                    var bottom = FindChildComponentViewModel(cell.Bottom);
                    if (bottom != null)
                    {
                        center.Bottom = null;
                        bottom.Top = null;
                    }
                }

                _canvas.Children.Remove(element);
                StopWatchingChild(element.DataContext as ComponentViewModel);
            }
        }

        void OnCircuitChanged()
        {
            foreach (var conductorViewModel in ConductorViewModels())
            {
                conductorViewModel.DisconnectConductors();
            }

            foreach (var conductorViewModel in ConductorViewModels())
            {
                conductorViewModel.ConnectConductors();
            }
        }

        IEnumerable<ComponentViewModel> ComponentViewModels()
        {
            foreach (var child in _canvas.Children)
            {
                var frameworkElement = child as FrameworkElement;
                if (frameworkElement != null)
                {
                    var componentViewModel = frameworkElement.DataContext as ComponentViewModel;
                    if (componentViewModel != null)
                    {
                        yield return componentViewModel;
                    }
                }
            }
        }

        IEnumerable<ConductorViewModel> ConductorViewModels()
        {
            foreach (var child in _canvas.Children)
            {
                var frameworkElement = child as FrameworkElement;
                if (frameworkElement != null)
                {
                    var conductorViewModel = frameworkElement.DataContext as ConductorViewModel;
                    if (conductorViewModel != null)
                    {
                        yield return conductorViewModel;
                    }
                }
            }
        }

        void OnGridSizeChanged(object sender, SizeChangedEventArgs e)
        {
            DrawLines(e.NewSize.Height, e.NewSize.Width);
        }

        private void DrawLines(double height, double width)
        {
            var brush = new SolidColorBrush(Colors.Red);

            _grid.Children.Clear();

            for (var y = 0; y < height; y += Cell.CellSize)
            {
                _grid.Children.Add(new Line { X1 = 0, X2 = width, Y1 = y, Y2 = y, Stroke = brush });
            }
            for (var x = 0; x < width; x += Cell.CellSize)
            {
                _grid.Children.Add(new Line { X1 = x, X2 = x, Y1 = 0, Y2 = height, Stroke = brush });
            }
        }

        void StartWatchingChild(ComponentViewModel componentViewModel)
        {
            if (componentViewModel != null)
            {
                componentViewModel.PropertyChanged += ComponentViewModelPropertyChanged;
            }
        }

        void StopWatchingChild(ComponentViewModel componentViewModel)
        {
            if (componentViewModel != null)
            {
                componentViewModel.PropertyChanged += ComponentViewModelPropertyChanged;
            }
        }

        void ComponentViewModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName.CompareTo("Rotation") == 0)
            {
                OnCircuitChanged();
            }
        }

        public FrameworkElement SelectedComponent
        {
            get { return (FrameworkElement)GetValue(SelectedComponentProperty); }
            set { SetValue(SelectedComponentProperty, value); }
        }

        public static readonly DependencyProperty SelectedComponentProperty =
            DependencyProperty.Register("SelectedComponent", typeof(FrameworkElement),
            typeof(MyCanvas), new PropertyMetadata(null));
    }
}
